package com.mall.common.config.security;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.mall.common.resp.ResponseResult;
import com.mall.common.resp.ResponseStatusCode;
import com.mall.service.UserService;
import com.mysql.cj.exceptions.PasswordExpiredException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.CorsUtils;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private UserService userService;
    @Autowired
    private ObjectMapper objectMapper;
    @Autowired
    private CaptchaFilter captchaFilter;


    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.cors().and().csrf().disable().authorizeRequests()
                //处理跨域请求中的Preflight请求
                .requestMatchers(CorsUtils::isPreFlightRequest).permitAll();
        //先进行验证码验证
        http.addFilterBefore(captchaFilter, UsernamePasswordAuthenticationFilter.class);
        http.authenticationProvider(authenticationProvider())
                .httpBasic()
                //未登录时 , 自定义响应结果
                .authenticationEntryPoint((request, response, ex) -> {
                    customErrorResponse(request,response, ResponseStatusCode.NO_AUTHORITY, ex);
                })
                .and()
                .authorizeRequests()
                //start
//                .anyRequest()  所有请求拦截
//                .antMatchers().hasRole("ADMIN")
//                .antMatchers().hasRole("ADMIN")
//                .antMatchers().hasRole("ADMIN")
//                .antMatchers()
                //end
                .antMatchers("/swagger-ui/**").hasRole("ADMIN")
                .mvcMatchers("/category/**","/swagger-ui/**","/user/**","/favorite/**","/address/**","/cart/**","/order/**")
                .authenticated()
                .and()
                .exceptionHandling()
                //没有权限，自定义响应结果
                .accessDeniedHandler((request, response, ex) -> {
                    customErrorResponse(request,response, ResponseStatusCode.NO_AUTHORITY, ex);
                })
                .and()
                .logout()
                //退出成功，自定义响应结果
                .logoutSuccessHandler((request, response, authentication) -> {
                    customSuccessResponse(request,response, ResponseStatusCode.SUCCESS, authentication);
                }).permitAll()
                //异常处理(权限拒绝、登录失效等)
                .and().exceptionHandling()
                //匿名用户访问无权限资源时的异常处理
                .authenticationEntryPoint((request, response, ex) -> {
                    String requestURI = request.getRequestURI();
                    int endIndex = requestURI.indexOf("/",1);
                    String substring = requestURI.substring(1, endIndex == -1 ? requestURI.length() : endIndex);
                    if(!"swagger-ui".equals(substring)){
                        customErrorResponse(request,response, ResponseStatusCode.NO_AUTHORITY, ex);
                    }else{
                        //warn 这里不知道怎么跳转到默认的登录页面了 , 又重新写了个页面进行登录
                        response.sendRedirect("/mall/login");
                    }
                });

        http
                .formLogin()
                //发送Ajax请求的路径
                .loginProcessingUrl("/login")
                //将用户名参数更改为email
                .usernameParameter("email")
                //验证失败处理
                .failureHandler((request, response, ex) -> {
                    if(ex instanceof UsernameNotFoundException){
                        customErrorResponse(request,response, ResponseStatusCode.USER_NOT_FOUND, ex);
                    }else if(ex instanceof BadCredentialsException){
                        customErrorResponse(request,response, ResponseStatusCode.USERNAME_OR_PASSWORD_ERROR, ex);
                    }else{
                        customErrorResponse(request,response, ResponseStatusCode.LOGIN_FAILED, ex);
                    }
                })
                //验证用户名之后逻辑
                .successHandler((request, response, authentication) -> {
                    customSuccessResponse(request,response, ResponseStatusCode.SUCCESS, authentication);
                })
                .permitAll();
        //开启跨域访问
        http.cors().disable();
        //开启模拟请求，比如API POST测试工具的测试，不开启时，API POST为报403错误
        http.csrf().disable();
    }

    @Bean
    public AuthenticationProvider authenticationProvider() {
        DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
        //对默认的UserDetailsService进行覆盖
        authenticationProvider.setUserDetailsService(userService);
        authenticationProvider.setPasswordEncoder(bCryptPasswordEncoder());

        return authenticationProvider;
    }

    @Bean("bCryptPasswordEncoder")
    public BCryptPasswordEncoder bCryptPasswordEncoder(){
        return  new BCryptPasswordEncoder();
    }

    private void initResponse(HttpServletRequest request,HttpServletResponse response){
        response.setContentType("application/json;charset=utf-8");
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.setHeader("Access-Control-Allow-Headers", "Authorization, Content-Type, X-Requested-With, token");
        response.setHeader("Access-Control-Allow-Methods", "GET, HEAD, OPTIONS, POST, PUT, DELETE");
        response.setHeader("Access-Control-Max-Age", "3600");
        response.setHeader("Access-Control-Allow-Origin", request.getHeader("Origin"));
        response.setStatus(200);
    }

    private void customSuccessResponse(HttpServletRequest request,HttpServletResponse response, ResponseStatusCode rsc, Authentication authentication) throws IOException {
        initResponse(request,response);
        PrintWriter out = response.getWriter();
        out.write(objectMapper.writeValueAsString(new ResponseResult<>(rsc, authentication)));
        out.flush();
        out.close();
    }
    private void customErrorResponse(HttpServletRequest request,HttpServletResponse response, ResponseStatusCode rsc, Exception e) throws IOException {
        initResponse(request,response);
        PrintWriter out = response.getWriter();
        out.write(objectMapper.writeValueAsString(new ResponseResult<>(rsc, e.getCause())));
        out.flush();
        out.close();
    }
    private void customResponse(HttpServletRequest request,HttpServletResponse response, ResponseResult responseResult) throws IOException {
        initResponse(request,response);
        PrintWriter out = response.getWriter();
        out.write(objectMapper.writeValueAsString(responseResult));
        out.flush();
        out.close();
    }
}
